package edu.gmu.middleware;

import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * The ByteBufferPool keeps a list of ByteBuffer objects that can
 * be checked out for exclusive use and returned when no longer 
 * needed - in fact, MUST be returned when no longer needed.
 * 
 * @author  James H. Pope
 * @version $Revision:$ $Date:$
 */
public class ByteBufferPool //implements Message
{
    /**
     * pool of buffers.
     */
    private LinkedList pool = new LinkedList();
    
    /**
     * lock to prevent simulteaous checkin/checkout
     */
    public Lock lock                 = new ReentrantLock(true); // make fair
    //public Condition bufferAvailable = lock.newCondition();
    
    /**
     * Creates a new ByteBufferPool with default parameters.
     * @param startingSize
     * @param bufferSize
     */
    public ByteBufferPool( int startingSize, int bufferSize )
    {
        for (int i = 0; i < startingSize; i++)
        {
            this.pool.add( ByteBuffer.allocate(bufferSize) );
        }
    }
    
//    /**
//     * Gets singleton instance of this object.
//     * @return instance
//     */
//    public static ByteBufferPool getInstance()
//    {
//        if( INSTANCE == null )
//        {
//            INSTANCE = new ByteBufferPool(DEFAULT_POOL_SIZE, DEFAULT_BUFFER_SIZE);
//        }
//        return INSTANCE;
//    }
    
    /**
     * Gets the size
     * @return size
     */
    public int getSize( )
    {
        return this.pool.size();
    }
    
    /**
     * Determines if no available buffer is in pool
     * @return true if empty, false if one or more available buffers
     */
    public boolean isEmpty( )
    {
        return (this.pool.size() == 0);
    }
    
    /**
     * Gets the new free buffer (pool no longer references). If no
     * buffer is available, the caller is blocked until a checkin occurs.
     * @return buffer or null if none is avaiable
     */
    public ByteBuffer checkout( )
    {
        this.lock.lock();
        try
        {
            if( isEmpty() ) return null;
            return (ByteBuffer)this.pool.removeFirst();
        }
        finally
        {
            this.lock.unlock();
        }
    }
    
    /**
     * Returns buffer to pool (caller should no longer reference)
     * @param buffer
     */
    public void checkin( ByteBuffer buffer )
    {
        this.lock.lock();
        try
        {
            this.pool.add(buffer);
        }
        finally
        {
            this.lock.unlock();
        }
    }
    
    public String toString( )
    {
        return "ByteBufferPool: size=" + this.pool.size();
    }
    
}
